# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2023/4/25 20:47
# @Author : xyjiang1216
# @Email : jiangxuyan@nudt.edu.cn
# @File : itp_algo.py
# @Version: OpenPlanner2.0
import time
from typing import List

from OpenPlanner.core.api.solver_interface import PlanningStrategy
from OpenPlanner.examples.itp_demo.itp_algo_input import ITPLink
from OpenPlanner.examples.itp_demo.itp_global_variables import CQF_QUEUE_LEN, TEST_NUM
from OpenPlanner.examples.itp_demo.itp_tabu import globalResource, schedSet, itp_tabu_sched_out_tradeoff
from OpenPlanner.examples.itp_demo.itp_utils import _itp_init_planning_graph, _itp_init_sched_set, _itp_init_global_queue_resource


def _compute_resource_ratio(g_resource: globalResource,
                            link_array: List[ITPLink]):
    ratio = 0
    total_link = len(link_array)
    for link in link_array:
        link_id = link.link_id
        for k in range(g_resource.cur_total_sched_slot_num):
            if g_resource.cqf_resource[link_id][k].used_len > CQF_QUEUE_LEN:
                print("link: %d, slot: %d, used_len: %d, free_len: %d"
                      % (link_id, k, g_resource.cqf_resource[link_id][k].used_len,
                         g_resource.cqf_resource[link_id][k].free_len))
            ratio += g_resource.cqf_resource[link_id][k].used_len

    return ratio / (CQF_QUEUE_LEN * g_resource.cur_total_sched_slot_num * total_link)


def static_schedule_restart(link_array,
                            stream_array,
                            instance_array,
                            solution_txt,
                            **kwargs
                            ):
    # 调度集合
    # tsn_sched_set
    # tabu参数
    rand_prob = kwargs['rand_prob']
    # rand_prob = float(solution_txt)
    # rand_prob = solution_txt.split('_')[-1]
    # rand_prob = float(rand_prob) / 10
    # print('rand_prob: %f' % rand_prob)

    slot = 125

    # 统计数据
    duration = [0.0 for i in range(TEST_NUM)]
    total_loop_num = [0 for i in range(TEST_NUM)]
    suc_num = [0 for i in range(TEST_NUM)]
    resource_ratio = [0.0 for i in range(TEST_NUM)]

    for i in range(TEST_NUM):
        # _itp_init_planning_graph(link_array, stream_array, instance_array, slot)
        # print([stream_obj.__dict__ for stream_obj in stream_array])

        start = time.time_ns()
        print('start...')

        # 第二步：初始化tsn_sched_set
        # tsn_sched_set = _itp_init_sched_set(stream_array, tsn_sched_set)
        tsn_sched_set: schedSet = _itp_init_sched_set(stream_array)
        print('_itp_init_sched_set done')

        # 第三步：初始化全局队列资源
        g_resource: globalResource = \
            _itp_init_global_queue_resource(link_array, stream_array, slot)
        print('_itp_init_global_queue_resource done')
        # cqf_resource = g_resource.cqf_resource
        # for lr in cqf_resource:
        #     for cqf in lr:
        #         print(cqf.__dict__)

        # 第三步：求解
        # tsn_sched_set和g_resource都被改变
        print('enter itp_tabu_sched_out_tradeoff')
        loop_num = itp_tabu_sched_out_tradeoff(link_array,
                                               stream_array,
                                               instance_array,
                                               slot,
                                               tsn_sched_set,
                                               g_resource,
                                               rand_prob)
        finish = time.time_ns()

        duration[i] = (finish - start) / 1000000000
        total_loop_num[i] = loop_num
        suc_num[i] = tsn_sched_set.cur_suc_num
        resource_ratio[i] = _compute_resource_ratio(g_resource,
                                                    link_array)
        count = 0

        for link_obj in link_array:
            # print(link_obj.stream_array)
            setattr(link_obj, 'solution', [])

        for j in range(tsn_sched_set.cur_suc_num):
            flow_id = tsn_sched_set.sched[j].flow_id
            offset = tsn_sched_set.sched[j].offset
            route = stream_array[flow_id].route
            hop = 0
            for link_id in route:
                link_array[link_id].solution.append(
                    {'stream_id': flow_id,
                     'period_id': 0,
                     'offset': offset + hop}
                )
                hop += 1
        for link_obj in link_array:
            link_obj.solution.sort(key=lambda x: (x['offset'], x['stream_id']))
        # for j in range(tsn_sched_set.cur_suc_num):
        #     print(tsn_sched_set.sched[j].offset)
        # with open(solution_txt, 'a') as fd:
        with open(solution_txt, 'a') as fd:
            for link_obj in link_array:
                fd.write('=' * 32 + '\n')
                fd.write('offset set at link_id: %d\n' % link_id)
                link_id = link_obj.link_id
                for item in link_obj.solution:
                    fd.write('{:>15}{}{:^15}{}\n'.format('stream_id:', '=', item['stream_id'], '|'))
                    fd.write('{:>15}{}{:^15}{}\n'.format('period_id:', '=', item['period_id'], '|'))
                    fd.write('{:>15}{}{:^15}{}\n'.format('offset:', '=', item['offset'], '|'))
                    fd.write('-' * (32 - 1) + '|' + '\n')

        print("TEST_NUM: %d, Restart [%d]th: slot_cycle: %d, sched_cycle: %d, suc_num: %d"
              % (i, count, slot, g_resource.cur_total_sched_slot_num, tsn_sched_set.cur_suc_num))

        print("TEST_NUM: %d, Restart [%d]th: flow_num: %d, loop_num: %d, time_consumed: %lf, resource_ratio: %f"
              % (i, count, tsn_sched_set.cur_flow_num, loop_num, duration[i], resource_ratio[i]))

        with open(solution_txt, 'a') as fd:
            fd.write("TEST_NUM: %d, Restart [%d]th: slot_cycle: %d, sched_cycle: %d, suc_num: %d\n"
                     % (i, count, slot, g_resource.cur_total_sched_slot_num, tsn_sched_set.cur_suc_num))

            fd.write(
                "TEST_NUM: %d, Restart [%d]th: flow_num: %d, loop_num: %d, time_consumed: %lf, resource_ratio: %f\n"
                % (i, count, tsn_sched_set.cur_flow_num, loop_num, duration[i], resource_ratio[i]))

    '''统计'''
    min_suc_num = min(suc_num)
    max_suc_num = max(suc_num)
    ave_suc_num = sum(suc_num) / TEST_NUM

    min_loop_num = min(total_loop_num)
    max_loop_num = max(total_loop_num)
    ave_loop_num = sum(total_loop_num) / TEST_NUM

    min_duration = min(duration)
    max_duration = max(duration)
    ave_duration = sum(duration) / TEST_NUM

    min_resource_ratio = min(resource_ratio)
    max_resource_ratio = max(resource_ratio)
    ave_resource_ratio = sum(resource_ratio) / TEST_NUM

    print("SUM Restart: flow_num: %d, cur_slot_cycle: %d"
          % (len(stream_array), g_resource.cur_total_sched_slot_num))
    print("SUM Restart: min_suc_num: %d, max_suc_num: %d, ave_suc_num: %d"
          % (min_suc_num, max_suc_num, ave_suc_num))
    print("SUM Restart: min_loop_num: %d, max_loop_num: %d, ave_loop_num: %d"
          % (min_loop_num, max_loop_num,
             ave_loop_num))
    print("SUM Restart: min_duration: %lf, max_duration: %lf, ave_duration: %lf"
          % (min_duration, max_duration,
             ave_duration))
    print("SUM Restart: min_resource_ratio: %f, max_resource_ratio: %f, ave_resource_ratio: %f"
          % (min_resource_ratio,
             max_resource_ratio, ave_resource_ratio))

    with open(solution_txt, 'a') as fd:
        fd.write("SUM Restart: flow_num: %d, cur_slot_cycle: %d\n"
                 % (len(stream_array), g_resource.cur_total_sched_slot_num))
        fd.write("SUM Restart: min_suc_num: %d, max_suc_num: %d, ave_suc_num: %d\n"
                 % (min_suc_num, max_suc_num, ave_suc_num))
        fd.write("SUM Restart: min_loop_num: %d, max_loop_num: %d, ave_loop_num: %d\n"
                 % (min_loop_num, max_loop_num,
                    ave_loop_num))
        fd.write("SUM Restart: min_duration: %lf, max_duration: %lf, ave_duration: %lf\n"
                 % (min_duration, max_duration,
                    ave_duration))
        fd.write("SUM Restart: min_resource_ratio: %f, max_resource_ratio: %f, ave_resource_ratio: %f\n"
                 % (min_resource_ratio,
                    max_resource_ratio, ave_resource_ratio))


class ITPAlgo(PlanningStrategy):
    def do_planning(self):
        static_schedule_restart(
            self.link_array,
            self.stream_array,
            self.instance_array,
            self.solution_txt,
            **self.kwargs
        )
